
/****************** item_25: 考虑写出一个不抛出异常的 swap 函数 *********************/

// swap典型实现
namespace std {
    template<typename T>
    void swap(T& a, T& b)
    {
        T temp(a);
        a = b;
        b = temp;
    }
}

// pimpl（pointer to implementation）手法：以指针指向一个对象，内含真正的数据
// example:

#include <vector>
class WidgetImpl {
    public:
    // ...
    private:
    int a, b, c;
    std::vector<double> v;
};

class Widget {
    public:
        Widget(const Widget& rhs);
        Widget& operator=(const Widget& rhs)
        {
            // ...
            *pImpl = *(rhs.pImpl);
            // ...
        }
    private:
        WidgetImpl* pImpl;
};

// swap 两个Widget 对象值，我们唯一需要做的就是置换其pImpl 指针，基本构想如下：
namespace std {
    template<>                      // std::swap 全特化版本
    void swap<Widget>(Widget& a,    // 对 T 是 Widget 的特化版本
                        Widget& b)
    {
        swap(a.pImpl, b.pImpl);     // 编译不过，pImpl 是 private 的
    }
}

// ps: template<> 表示它是 std::swap 的一个全特化版本，函数名之后的 <Widget> 表示这一特化版本系针对 
// "T 是 Widget" 而设计
// 真正设计如下：
class Widget {
    public:
    Widget(const Widget& rhs);
    Widget& operator=(const Widget& rhs)
    {
        // ...
        *pImpl = *(rhs.pImpl);
        // ...
    }

    void swap(Widget& other)        // Widget 中 swap 成员函数
    {
        using std::swap;
        swab(pImpl, other.pImpl);
    }

    private:
        WidgetImpl* pImpl;
};

namespace std {
    template<>                              // 修订后的 std::swap 特化版本
    void swap<Widget>(Widget& a, Widget& b) {
        a.swap(b);
    }
}

// 如果 Widget 和 WidgetImpl 都是 class templates 而非 classes 可以定义成如下形式：
namespace WidgetStuff {
    // ...
    template<typename T>
    class Widget            // 同前，内含 swap 的成员函数
    {
        // ...
    };
    // ...
    template<typename T>                    // non-member swap 函数；
    void swap(Widget<T>& a, Widget<T>& b)   // 这里并不属于 std 命名空间
    {
        a.swap(b);
    }
}

// 总结：
// 1. 当 std::swap 对你的类型效率不高时，提供一个 swap 成员函数，并确定这个函数不抛出异常。
// 2. 如果你提供一个 number swap，也该提供一个 non-member swap 用来调用前者。对于 classes（而非 templates），也请特化 std::swap。
// 3. 调用 swap 时应针对 std::swap 使用using声明式，然后调用 swap 并且不带任何 "命名空间资格修饰"。
// 4. 为 "用户定义类型"进行 std templates 全特化的好的，但千万不要尝试在 std 内加入某些对 std 而言全新的东西。